package com.wanban.download.db

import android.app.Application
import android.content.ContentValues
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase
import android.text.TextUtils
import android.util.Log
import androidx.core.database.getFloatOrNull
import androidx.core.database.getIntOrNull
import androidx.core.database.getLongOrNull
import androidx.core.database.getStringOrNull
import com.wanban.download.model.Status
import com.wanban.download.utils.CacheM3U8Utils
import java.util.Arrays

import java.util.concurrent.atomic.AtomicInteger

class DatabaseManager {

    private val mOpenCounter = AtomicInteger()
    private var mDatabase: SQLiteDatabase? = null

    private val column = arrayOf(
        DownloadSQLiteHelper.Columns.URL,
        DownloadSQLiteHelper.Columns.VIDEO_NAME,
        DownloadSQLiteHelper.Columns.VIDEO_ID,
        DownloadSQLiteHelper.Columns.STATE,
        DownloadSQLiteHelper.Columns.EPISODE,
        DownloadSQLiteHelper.Columns.SOURCE_ID,
        DownloadSQLiteHelper.Columns.CATEGORY,
        DownloadSQLiteHelper.Columns.PERCENT,
        DownloadSQLiteHelper.Columns.WATCH_TIME,
        DownloadSQLiteHelper.Columns.CREATE_TIME,
        DownloadSQLiteHelper.Columns.DOWNLOAD_TYPE,
        DownloadSQLiteHelper.Columns.CONTENT_LENGTH,
        DownloadSQLiteHelper.Columns.PLAY_PATH,
        DownloadSQLiteHelper.Columns.ERROR_MESSAGE,
        DownloadSQLiteHelper.Columns.HEADER,
        DownloadSQLiteHelper.Columns.COVER_URL,
        DownloadSQLiteHelper.Columns.FILE_ROOT_PATH,
        DownloadSQLiteHelper.Columns.EPISODE_NAME,
        DownloadSQLiteHelper.Columns.EXTRA,
        DownloadSQLiteHelper.Columns.TEMP_CONTENT_LENGTH,
        DownloadSQLiteHelper.Columns.COVER_FILE_PATH,
        DownloadSQLiteHelper.Columns.SKITS_STATUS
    )

    //写
    @Synchronized
    fun openWritableDatabase(): SQLiteDatabase? {
        if (mOpenCounter.incrementAndGet() == 1) {
            mDatabase = mDatabaseHelper?.writableDatabase
        }
        return mDatabase
    }

    @get:Synchronized
    val readableDatabase: SQLiteDatabase?
        //读
        get() {
            if (mOpenCounter.incrementAndGet() == 1) {
                // Opening new database
                mDatabase = mDatabaseHelper?.readableDatabase
            }
            return mDatabase
        }

    //同步关闭
    @Synchronized
    fun close() {
        if (mOpenCounter.decrementAndGet() == 0) {
            mDatabase?.close()
        }
    }

    @Synchronized
    fun getTask(selection: String?,
                selectionArgs: Array<String>?,
                isContainerM3U8: Boolean = false): DownloadEntity? {
        val db = readableDatabase?:return null
        var cursor: Cursor? = null
        try {
            db.beginTransaction()
            cursor = db.query(
                DownloadSQLiteHelper.TABLE_VIDEO_DOWNLOAD_INFO,
                column, selection, selectionArgs,
                null, null, null, null
            )
            db.setTransactionSuccessful()
            if (cursor.moveToFirst()) {
                return getDataByCursor(cursor, isContainerM3U8)
            }
            return null
        } catch (e: Exception) {
            e.printStackTrace()
            return null
        } finally {
            cursor?.close()
            db.endTransaction()
            close()
        }
    }


    @Synchronized
    fun getTasks(selection: String? = null,
                 selectionArgs: Array<String>? = null,
                 isContainerM3U8: Boolean = false): ArrayList<DownloadEntity>? {
        val db = readableDatabase?:return null
        var cursor: Cursor? = null
        try {
            db.beginTransaction()
            cursor = db.query(
                DownloadSQLiteHelper.TABLE_VIDEO_DOWNLOAD_INFO,
                column, selection, selectionArgs,
                null, null, null, null)
            val items = ArrayList<DownloadEntity>()
            db.setTransactionSuccessful()
            while (cursor.moveToNext()) {
                val item = getDataByCursor(cursor, isContainerM3U8)?:continue
                items.add(item)
            }
            return items
        } catch (e: Exception) {
            e.printStackTrace()
            return null
        } finally {
            cursor?.close()
            db.endTransaction()
            close()
        }
    }



    @Synchronized
    private fun query(db: SQLiteDatabase, url: String): String? {
        var cursor: Cursor? = null
        return try {
            db.beginTransaction()
            cursor = db.query(DownloadSQLiteHelper.TABLE_VIDEO_DOWNLOAD_INFO,
                arrayOf(DownloadSQLiteHelper.Columns.URL),
                DownloadSQLiteHelper.Columns.URL + " = ?",
                arrayOf(url), null, null, null)
            db.setTransactionSuccessful()
            if (cursor.moveToNext()) {
                cursor.getStringOrNull(cursor.getColumnIndexOrThrow(DownloadSQLiteHelper.Columns.URL))
            } else {
                null
            }
        } catch (e: Exception) {
            e.printStackTrace()
            null
        } finally {
            db.endTransaction()
            cursor?.close()
        }
    }

    @Synchronized
    fun insert(task: DownloadEntity, isUpdateM3U8: Boolean = false): Long{
        var index = -1L
        if (TextUtils.isEmpty(task.url)) return index
        val db = openWritableDatabase() ?: return index
        return try {
            // 先查是否已经存在当前任务
            val taskUrl = query(db, task.url!!)
            val values = getContentValues(task, isUpdateM3U8)
            db.beginTransaction()
            index = if (TextUtils.isEmpty(taskUrl)) { // 表中无此数据，需要插入
                db.insert(DownloadSQLiteHelper.TABLE_VIDEO_DOWNLOAD_INFO, null, values)
            } else { // 已经在表中，无需再次插入，需要更新
                db.update(DownloadSQLiteHelper.TABLE_VIDEO_DOWNLOAD_INFO, values,
                    DownloadSQLiteHelper.Columns.URL+"=?", arrayOf(task.url)).toLong()
            }
            db.setTransactionSuccessful()
            index
        } catch (e: Exception) {
            e.printStackTrace()
            index
        } finally {
            db.endTransaction()
            close()
        }
    }


    @Synchronized
    fun delete(url: String): Long{
        val db = openWritableDatabase() ?: return -1L
        return try {
            db.beginTransaction()
            val index = db.delete(DownloadSQLiteHelper.TABLE_VIDEO_DOWNLOAD_INFO, DownloadSQLiteHelper.Columns.URL +"=?", arrayOf(url)).toLong()
            db.setTransactionSuccessful()
            index
        } catch (e: Exception) {
            e.printStackTrace()
            -1L
        } finally {
            db.endTransaction()
            close()
        }
    }

    @Synchronized
    fun deleteAll(){
        val db = openWritableDatabase() ?: return
        try {
            db.beginTransaction()
            db.delete(DownloadSQLiteHelper.TABLE_VIDEO_DOWNLOAD_INFO, null, null).toLong()
            db.setTransactionSuccessful()
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            db.endTransaction()
            close()
        }
    }

    private fun getDataByCursor(cursor: Cursor, isContainerM3U8: Boolean): DownloadEntity? {
        val url = cursor.getStringOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.URL))
        if (TextUtils.isEmpty(url)) return null
        val videoName = cursor.getStringOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.VIDEO_NAME))
        val item = DownloadEntity(url, videoName)
        item.state = cursor.getIntOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.STATE))?: Status.IDLE
        item.videoId = cursor.getLongOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.VIDEO_ID))?:-1L
        item.episode = cursor.getIntOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.EPISODE))?:0
        item.sourceId = cursor.getIntOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.SOURCE_ID))?:0
        item.category = cursor.getIntOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.CATEGORY))?:0
        item.percent = cursor.getFloatOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.PERCENT))?:0f
        item.watchTime = cursor.getLongOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.WATCH_TIME))?:0
        item.createTime = cursor.getLongOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.CREATE_TIME))?:0L
        item.downloadType = cursor.getIntOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.DOWNLOAD_TYPE))?:0
        item.contentLength = cursor.getLongOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.CONTENT_LENGTH))?:0L
        item.playPath = cursor.getStringOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.PLAY_PATH))
        item.errorMsg = cursor.getStringOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.ERROR_MESSAGE))
        item.coverUrl = cursor.getStringOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.COVER_URL))
        item.coverFilePath = cursor.getStringOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.COVER_FILE_PATH))
        item.episodeName = cursor.getStringOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.EPISODE_NAME))
        item.fileRootPath = cursor.getStringOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.FILE_ROOT_PATH))
        item.extra = cursor.getStringOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.EXTRA))
        item.tempContentLength = cursor.getLongOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.TEMP_CONTENT_LENGTH))?:0L
        item.skitsStatus = cursor.getIntOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.SKITS_STATUS))?:0

        // header需要转换
        val headerStr = cursor.getStringOrNull(cursor.getColumnIndex(DownloadSQLiteHelper.Columns.HEADER))
        if (!TextUtils.isEmpty(headerStr)) {
            item.header = string2Map(headerStr)
        }
        if (isContainerM3U8) { // 包含m3u8
            if (item.state != Status.SUCCESS) { // 下载成功的不会再次查询m3u8,因为没必要
                item.m3u8 = CacheM3U8Utils.getCacheM3U8(item.downloadTag)
            }
        }
        return item
    }


    /**
     * 获取插入或更新的数据
     * @param isUpdateM3U8 只有insert的时候存在，update的时候不会处理
     */
    private fun getContentValues(item: DownloadEntity, isUpdateM3U8: Boolean): ContentValues {
        val values = ContentValues()
        values.put(DownloadSQLiteHelper.Columns.URL, item.url)
        values.put(DownloadSQLiteHelper.Columns.VIDEO_NAME, item.videoName)
        values.put(DownloadSQLiteHelper.Columns.STATE, item.state)
        values.put(DownloadSQLiteHelper.Columns.VIDEO_ID, item.videoId)
        values.put(DownloadSQLiteHelper.Columns.EPISODE, item.episode)
        values.put(DownloadSQLiteHelper.Columns.SOURCE_ID, item.sourceId)
        values.put(DownloadSQLiteHelper.Columns.CATEGORY, item.category)
        values.put(DownloadSQLiteHelper.Columns.PERCENT, item.percent)
        values.put(DownloadSQLiteHelper.Columns.WATCH_TIME, item.watchTime)
        values.put(DownloadSQLiteHelper.Columns.CREATE_TIME, item.createTime)
        values.put(DownloadSQLiteHelper.Columns.DOWNLOAD_TYPE, item.downloadType)
        values.put(DownloadSQLiteHelper.Columns.CONTENT_LENGTH, item.contentLength)
        values.put(DownloadSQLiteHelper.Columns.PLAY_PATH, item.playPath)
        values.put(DownloadSQLiteHelper.Columns.COVER_URL, item.coverUrl)
        values.put(DownloadSQLiteHelper.Columns.ERROR_MESSAGE, item.errorMsg)
        values.put(DownloadSQLiteHelper.Columns.COVER_FILE_PATH, item.coverFilePath)
        values.put(DownloadSQLiteHelper.Columns.EPISODE_NAME, item.episodeName)
        values.put(DownloadSQLiteHelper.Columns.FILE_ROOT_PATH, item.fileRootPath)
        values.put(DownloadSQLiteHelper.Columns.EXTRA, item.extra)
        values.put(DownloadSQLiteHelper.Columns.TEMP_CONTENT_LENGTH, item.tempContentLength)
        values.put(DownloadSQLiteHelper.Columns.SKITS_STATUS, item.skitsStatus)
        if (item.header != null) {
            values.put(DownloadSQLiteHelper.Columns.HEADER, map2String(item.header))
        }
        if (isUpdateM3U8 && item.m3u8 != null) {
            Log.e("ExchangeManager -> ", "新表缓存m3u8 -> " + item.m3u8.tsList.size)
            // 本地化
            CacheM3U8Utils.cacheM3U8(item.downloadTag, item.m3u8)
        }
        return values
    }

    private fun map2String(map: Map<String, String>?): String?{
        if (map == null) return null
        //将set集合转换为数组
        val keyArray = map.keys.toTypedArray()
        //给数组排序(升序)
        Arrays.sort(keyArray)
        //因为String拼接效率会很低的，所以转用StringBuilder
        val sb = StringBuilder()
        for (i in keyArray.indices) {
            // 参数值为空，则不参与签名 这个方法trim()是去空格
            val key = keyArray[i]
            val value = map[key]?.trim()
            if (!TextUtils.isEmpty(value)) {
                sb.append(key)
                sb.append(":")
                sb.append(value)
            }
            if (i != keyArray.size - 1) {
                sb.append(",")
            }
        }
        return sb.toString()
    }

    private fun string2Map(str: String?): HashMap<String, String>? {
        if (str == null || TextUtils.isEmpty(str)) return null
        //根据逗号截取字符串数组
        val mapStr = str.split(",")
        //创建Map对象
        val map = HashMap<String, String>()
        //循环加入map集合
        //循环加入map集合
        for (i in mapStr.indices) {
            //根据":"截取字符串数组
            val keyValueArray = mapStr[i].split(":")
            if (keyValueArray.size < 2) continue
            //str2[0]为KEY,str2[1]为值
            map[keyValueArray[0]] = keyValueArray[1]
        }
        return map
    }

    //强制真正的关闭
    @Synchronized
    fun trueClose() {
        mDatabase?.close()
    }

    companion object {
        private var instance: DatabaseManager? = null
        private var mDatabaseHelper: DownloadSQLiteHelper? = null

        //初始化
        @Synchronized
        fun initializeInstance(application: Application) {
            if (instance == null) {
                synchronized(DatabaseManager::class.java) {
                    if (null == instance) {
                        instance = DatabaseManager()
                        mDatabaseHelper = DownloadSQLiteHelper(application)
                    }
                }
            }
        }

        @Synchronized
        fun getInstance(): DatabaseManager? {
            checkNotNull(instance) {
                DatabaseManager::class.java.simpleName + " is not initialized, call initializeInstance(..) method first."
            }
            return instance
        }
    }
}